package com.example.mq.mqserver.core;

import com.example.mq.common.MqException;

import javax.xml.ws.BindingType;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: DongGuoZhen
 * @Date: 2024/03/04/14:12
 * @Description: 使用这个类来实现交换机的转发规则
 * 同时也借助这个类验证bindingKey是否合法
 */
public class Router {


    // 通过这个方法判定当前bindingKey是否合法
    //bindingKey的构造规则
    //1.数字 字母 下划线
    //2.使用.分割
    //3.运行存在 * 和 # 作为通配符 但是通配符只能作为独立的分段
    public boolean checkBindingKey(String bindingKey) {
        //判断是否为空
        if (bindingKey.length() == 0) {
            // 使用直接交换机的时候,bindingKey是用不到的. fanout扇出交换机也是用不上的
            return true;
        }
        //检查字符串中不能存在非法字符
        for (int i = 0; i < bindingKey.length(); i++) {
            char ch = bindingKey.charAt(i);
            if (ch >= 'A' && ch <= 'Z') {
                continue;
            }
            if (ch >= 'a' && ch <= 'z') {
                continue;
            }
            if (ch >= '0' && ch <= '9') {
                continue;
            }
            if (ch == '_' || ch == '.' || ch == '*' || ch == '#') {
                continue;
            }
            return false;
        }
        //检查 * 或者 # 号是否是独立的部分  aaa.*.bbb合法     aaa.a*.bbb非法

        String[] words = bindingKey.split("\\.");
        for (String word : words) {
            //检查word的长度大于1,并且包含了* 或者# 就是非法的
            if (word.length() > 1 && (word.contains("*") || word.contains("#"))) {
                return false;
            }
        }
        //约定一下通配符之间的相邻关系
        //前三种相邻的时候,实现匹配的逻辑会非常繁琐,同时功能性提升不大
        //1. aaa.#.#.bbb 非法
        //2. aaa.#.*.bbb 非法
        //3. aaa.*.#.bbb 非法
        //4. aaa.*.*.bbb 合法
        for (int i = 0; i < words.length - 1; i++) {
            //连续两个#
            if (words[i].equals("#") && words[i + 1].equals("#")) {
                return false;
            }
            //#后面有*
            if (words[i].equals("#") && words[i + 1].equals("*")) {
                return false;
            }
            //*后面有#
            if (words[i].equals("*") && words[i + 1].equals("#")) {
                return false;
            }
        }
        return true;
    }

    //routingKey检测
    //routingKey的构造规则
    // 1. 数字 字母 下划线
    // 2. 使用 . 分割若干
    public boolean checkRoutingKey(String routingKey) {
        //如果routingKey长度为0
        if (routingKey.length() == 0) {
            //空字符串,合法的情况 比如fanout交换机,routingKey是用不上的,就可以设置为空
            return true;
        }
        for (int i = 0; i < routingKey.length(); i++) {
            char ch = routingKey.charAt(i);
            //判定是否为大写字母
            if (ch >= 'A' && ch <= 'Z') {
                continue;
            }
            //判定该字母是否为小写
            if (ch >= 'a' && ch <= 'z') {
                continue;
            }
            //判断是否为 阿拉伯数字
            if (ch >= '0' && ch <= '9') {
                continue;
            }
            // 判定是否是下划线和 .
            if (ch == '_' || ch == '.') {
                continue;
            }
            //该字符不是上述任何一种合法情况,直接返回
            return false;
        }
        //把每个字符都检查过了,没有遇到非法情况,直接返回true;
        return true;
    }

    //这个方法用来判定该消息是否可以转发给绑定对应的队列
    public boolean route(ExchangeType exchangeType, Binding binding, Message message) throws MqException {
        //TODO
        //根据不同的exchangeType来使用不同的转发规则
        if (exchangeType == ExchangeType.FANOUT) {
            //如果是fanout类型,则该交换机上绑定的所有队列都要发送
            return true;
        } else if (exchangeType == ExchangeType.TOPIC) {
            //如果是topic主题交换机,规则复杂一下
            return routeTopic(binding, message);
        } else {
            //其他情况不应该存在
            throw new MqException("[Router] 交换机类型非法 exchangeType=" + exchangeType);
        }
    }

    //TOPIC的转发规则实现
    private boolean routeTopic(Binding binding, Message message) {
        //先把这个两个key进行切分
        String[] bindingTokens = binding.getBindingKey().split("\\.");
        String[] routingTokens = message.getRoutingKey().split("\\.");
        //引入两个下标,指向上述两个数组,初始情况都为0
        int bindingIndex = 0;
        int routingIndex = 0;
        while (bindingIndex < bindingTokens.length && routingIndex < routingTokens.length) {
            if (bindingTokens[bindingIndex].equals("*")) {
                //如果 * 直接进入下一个  因为可以匹配任意一个部分
                bindingIndex++;
                routingIndex++;
                continue;
            } else if (bindingTokens[bindingIndex].equals("#")) {
                // 如果遇到#号,需要先看看有没有下一个位置
                bindingIndex++;
                if (bindingIndex == bindingTokens.length) {
                    // 该#号后面没有东西了,此时一定能匹配
                    return true;
                }
                // #号后面还有东西,拿着这个内容去routingKey中往后找,找到对应的位置
                //findNextMath这个方法用来查找该部分在routingKey中的位置,并且返回该下标,没有找到,返回-1
                routingIndex = findNextMath(routingTokens, routingIndex, bindingTokens[bindingIndex]);
                if (routingIndex == -1) {
                    return false;
                }
                //找到了匹配的情况,继续往后匹配
                bindingIndex++;
                routingIndex++;
            } else {
                //如果遇到普通字符串,要求两边的内容是一样的,如果不一样就return false
                if (!bindingTokens[bindingIndex].equals(routingTokens[routingIndex])) {
                    return false;
                }
                bindingIndex++;
                routingIndex++;
            }
        }
        //判定是否是双方同时到达末尾了
        if(bindingIndex == bindingTokens.length && routingIndex==routingTokens.length) {
            return true;
        }
        return false;
    }

    private int findNextMath(String[] routingTokens, int routingIndex, String bindingToken) {
        for (int i = routingIndex; i < routingTokens.length; i++) {
            if(routingTokens[i].equals(bindingToken)) {
                return i;
            }
        }
        return -1;
    }
}
